#pragma once

#include "LuaContext.h"

using namespace System;
using namespace System::Reflection;
using namespace System::Collections::Generic;
using namespace System::Runtime::InteropServices;

namespace vJine {
	namespace Lua {
		ref class LuaHelper
		{
		public:
		ref class ParamInfo {
		public:
			Type^ type;
			const char* name;
		};

		public:
			ref class Property {
			internal:
				void* handle;

			public:
				Property(PropertyInfo^ p, String^ name);

				const char *name;
				Type^ type;
				MethodInfo^ getter;
				MethodInfo^ setter;

				Object^ get(Object^ context);
				void set(Object^ context, array<Object^>^ args);
			};
		public:
			ref class Method {
			internal:
				void* handle;

			public:
				Method(MethodInfo^ m, String^ name);

				const char* name;
				Type^ type;
				bool has_result;

				int arg_count;
				List<ParamInfo^>^ Params;
				MethodInfo^ caller;
			};
			
		public:
			generic <typename T>
			ref class Cache {
				static List<Property^>^ StaticProperties = gcnew List<Property^>();

				static List<Property^>^ InstanceProperties = gcnew List<Property^>();

				static List<Method^>^ StaticMethods = gcnew List<Method^>();
				static List<Method^>^ InstanceMethods = gcnew List<Method^>();

				static void getProperties(List<Property^>^ container, array<PropertyInfo^>^ P);

				static void getMethods(List<Method^>^ container, array<MethodInfo^>^ M);
			internal:
				static void* handle;
				static void* ctor;

			public:
				static Cache() {
					Type^ Tobj = T::typeid;
					Cache<T>::handle = LuaHelper::to_pointer(Tobj);
					Cache<T>::ctor = LuaHelper::to_pointer(Tobj->GetConstructor(Type::EmptyTypes));

					getProperties(StaticProperties,
						Tobj->GetProperties(BindingFlags::Public | BindingFlags::Static | BindingFlags::DeclaredOnly));
					getProperties(InstanceProperties,
						Tobj->GetProperties(BindingFlags::Public | BindingFlags::Instance | BindingFlags::DeclaredOnly));

					getMethods(StaticMethods,
						Tobj->GetMethods(BindingFlags::Public | BindingFlags::Static | BindingFlags::DeclaredOnly));
					getMethods(InstanceMethods,
						Tobj->GetMethods(BindingFlags::Public | BindingFlags::Instance | BindingFlags::DeclaredOnly));
					
				}
				
				static void set(lua_State* L, T context);
				static void get(lua_State* L, T context);

				static void reg(lua_State* L, String^ nameSpace);
			};

		internal:

			static Object^ to_object(void* ptr);
			static void* to_pointer(Object^ obj);
			static String^ get_table(lua_State*L, String^ nameSpace, bool create, bool ignore_last);
			static void get_field(lua_State*L, String^ nameSpace);
			static void set_field(lua_State*L, String^ nameSpace);
		public:

			static void set(lua_State *L, Object^ value);
			static void set(lua_State *L, Type^ type, Object^ value);
			static Object^ get(lua_State *L, Type^ type, int stack_index);
			static Object^ get(lua_State *L, int stack_index);

			generic <typename T>
			static void set(lua_State* L, T context);

			static void hook_property(lua_State* L, void* context, LuaHelper::Property^ p);
			static void hook_method(lua_State* L, void* context, LuaHelper::Method^ m);

			generic <typename T>
			static void get(lua_State* L, T context);

			static const char * get_chars(String^ name);
			static void free_chars(const char* chars);
		};
	}
}

